package com.itheima.advice;

import com.alibaba.fastjson.JSON;
import com.itheima.anno.Cache;
import com.itheima.anno.CacheEvict;
import com.itheima.anno.Perform;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisPool;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Set;

/**
 * @Author Chris
 * @Date 2020/9/26 20:46
 */
@Component
@Aspect
public class CacheAdvice {
    @Autowired
    private JedisPool jedisPool;

    @Pointcut("execution(* * ..service.*.*(..))")
    public void pt(){}

    /***
     * @Author Chris
     * @Description 缓存数据
     * @Param [pjp]
     * @return java.lang.Object
     **/
    @Around("pt()")
    public Object cacheAround(ProceedingJoinPoint pjp) throws Throwable {
        //获取当前方法class文件,方法名,全类名
        Class<?> clazz = pjp.getTarget().getClass();
        String methodName = pjp.getSignature().getName();
        String className = pjp.getSignature().getDeclaringTypeName();
        //获取参数数组(用于做rediskey),参数字节码数组
        Object[] args = pjp.getArgs();
        Signature signature =pjp.getSignature();
        Class[] parameterTypes = ((MethodSignature) signature).getParameterTypes();

        //获取method对象
        Method method = clazz.getMethod(methodName,parameterTypes);
        //返回值字节码
        Class<?> returnType = method.getReturnType();
        if (returnType==null){
            System.out.println("返回值不能为空");
            return pjp.proceed();
        }
        //获取注解
        Cache cache = method.getAnnotation(Cache.class);
        if (cache!=null){
            int time = cache.time();
            //从redis中查询是否有数据
            String key=className+"/"+methodName+"/"+ JSON.toJSONString(args);
            String value = jedisPool.getResource().get(key);
            if (value!=null){
                //有返回数据
                return JSON.parseObject(value, returnType);
            }
            //没有添加到redis中,返回查询数据库的值
            String result = JSON.toJSONString(pjp.proceed());
            jedisPool.getResource().setex(key,time,result);
        }
        return pjp.proceed();
    }
    
    /***
     * @Author Chris
     * @Description 删除缓存
     * @Param [pjp]
     * @return java.lang.Object
     **/
    @Around("pt()")
    public Object cacheEvictAround(ProceedingJoinPoint pjp) throws Throwable {
        //获取当前方法class文件,方法名,全类名
        Class<?> clazz = pjp.getTarget().getClass();
        String methodName = pjp.getSignature().getName();
        String className = pjp.getSignature().getDeclaringTypeName();
        Signature signature =pjp.getSignature();
        Class[] parameterTypes = ((MethodSignature) signature).getParameterTypes();

        //获取method对象
        Method method = clazz.getMethod(methodName,parameterTypes);
        CacheEvict cacheEvict = method.getAnnotation(CacheEvict.class);
        //注解不为空,删除redis中所有相关数据
        if (cacheEvict!=null){

            Set<String> keys = jedisPool.getResource().keys("*");
            keys.forEach((key)->{
                if (key.contains(className)){
                    jedisPool.getResource().del(key);
                }
            });
        }
        return pjp.proceed();
    }

    @Around("pt()")
    public Object performAround(ProceedingJoinPoint pjp) throws Throwable {
        //获取当前方法class文件,方法名,全类名
        Class<?> clazz = pjp.getTarget().getClass();
        String methodName = pjp.getSignature().getName();
        String className = pjp.getSignature().getDeclaringTypeName();
        Signature signature =pjp.getSignature();
        Class[] parameterTypes = ((MethodSignature) signature).getParameterTypes();

        //获取method对象
        Method method = clazz.getMethod(methodName,parameterTypes);
        Perform perform = method.getAnnotation(Perform.class);
        if (perform!=null){
            long start = System.currentTimeMillis();
            Object ret = pjp.proceed();
            long end = System.currentTimeMillis();
            System.out.println("当前方法:"+className+methodName+" 执行时间:"+(end-start)+"毫秒");
            return ret;
        }
        return pjp.proceed();
    }
}
